/**
 * 
 */
package org.linkminder;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import java.util.logging.Logger;

import org.linkminder.api.ILinkMinderDataProvider;
import org.linkminder.persist.LinkMinderDatabase;


/**
 * @author jackpark
 * <p>The core LinkMinder implementation.</p>
 * <p>In its simplest implementation, it is just a {@link Map}</p>
 * <p>In its more complex implementation, it relies on an RDBMS to
 * provide map functions, with a local map or LRU providing caching functions.</p>
 * <p>If a local {@link Map} is used as a pure cache, then scalability is compromised since
 * the map may end up containing far too many items for memory. An LRU (Least recently used) implementation
 * provides the same cache functionality but within the constraints of a fixed memory footprint. Least-recently-used
 * items are discarded (though they remain backed up in persistent store.</p>
 * <p>LinkMinder is the <em>Model</em> of a Model-View-Control pattern.</p>
 */
public class LinkMinder {
	private static final Logger log =
	      Logger.getLogger(LinkMinder.class.getName());
	
	/**
	 * We can eventually change these to config file settings
	 */
	private final String DB_NAME = "LinkMinder";
	private final String USER_NAME = "LinkUser";
	private final String PASSWORD = "";

	/**
	 * Simple implementation
	 */
	private Map<String,List<String>> localBrain = new HashMap<String,List<String>>();
	/**
	 * Eventual implementation
	 */
	private ILinkMinderDataProvider persistentBrain;
	/**
     * Pools Connections for each local thread
     * Must be closed when the thread terminates
     */
    private ThreadLocal <Connection>localConnection = new ThreadLocal<Connection>();

	/**
	 * @param dataPath--path to RDBMS data files
	 * @throws LinkMinderException
	 */
	public LinkMinder(String dataPath) throws LinkMinderException {
		log.info("LinkMinder- "+dataPath);
		// TODO boot persistentBrain
		//persistentBrain = new LinkMinderDatabase(this.DB_NAME, this.USER_NAME, this.PASSWORD, dataPath);
	}
	
	/**
	 * Deal with shutting down persistentBrain
	 */
	public void shutDown() {
		try {
			if (persistentBrain != null)
				persistentBrain.shutDown();
			this.closeLocalConnection();
		} catch (Exception e) {
			log.info("LinkMinder.shutDown error "+e.getMessage());
		}
	}

	/**
	 * can return an empty list
	 * @param idea
	 * @return does not return <code>null</code>
	 * @throws LinkMinderException
	 */
	List<String> getLocalIdea(String idea) throws LinkMinderException {
		synchronized(localBrain) {
			List<String> result = localBrain.get(idea);
			if (result == null) {
				result = new ArrayList<String>();
				localBrain.put(idea, result);
			}
			return result;
		}
	}

	/**
	 * 
	 * @param idea
	 * @param associatedIdea
	 * @return does not return <code>null</code>
	 * @throws LinkMinderException
	 */
	List<String> associateLocalIdeas(String idea, String associatedIdea) throws LinkMinderException {
		synchronized(localBrain) {
			List<String> result = getLocalIdea(idea);
			result.add(associatedIdea);
			localBrain.put(idea, result);
			List<String>temp = getLocalIdea(associatedIdea);
			temp.add(idea);
			localBrain.put(associatedIdea, temp);
			return result;
		}
	}
	
	///////////////////
	// TODO later
	///////////////////
	public List<String> getPersistentIdea(String idea) throws LinkMinderException {
		List<String> result = persistentBrain.get(getLocalConnection(), idea);
		return result;
	}

	public List<String> associatePersistentIdeas(String idea, String associatedIdea) throws LinkMinderException {
		List<String> result = getPersistentIdea(idea);
		if (result == null)
			result = persistentBrain.associate(getLocalConnection(), idea, associatedIdea);
		List<String>temp = getPersistentIdea(associatedIdea);
		if (temp == null)
			temp = persistentBrain.associate(getLocalConnection(), associatedIdea, idea);
		return result;
	}
	
  	private Connection getLocalConnection() throws LinkMinderException {
   	   synchronized(localConnection) {
             Connection con = this.localConnection.get();
             if (con == null) {
             	try {
             		con = persistentBrain.getConnection();
             		localConnection.set(con);
             	} catch (Exception e) {
             		log.info("FileModel.getLocalConnection error "+e.getMessage());
             		throw new LinkMinderException(e);
             	}
             }
             return con;
   	   }
     }

     public void closeLocalConnection() throws LinkMinderException {
          try {
       	   synchronized(localConnection) {
   	         Connection con = this.localConnection.get();
   	         if (con != null)
   	           con.close();
   	         localConnection.set(null);
       	   }
          } catch (SQLException e) {
            throw new LinkMinderException(e);
          }
      }


}
